BT-04: Integrate AuditLogger into EQMON Endpoints
Goal: Add AuditLogger calls to all security-relevant EQMON endpoints so every action is captured in the audit trail.
Files:
- Modify: /var/www/html/eqmon/lib/middleware.php β add logApiAccess() call
- Modify: /var/www/html/eqmon/api/auth/login.php β audit login events
- Modify: /var/www/html/eqmon/api/auth/logout.php β audit logout
- Modify: /var/www/html/eqmon/api/auth/forgot-password.php β audit password reset requests
- Modify: /var/www/html/eqmon/api/auth/reset-password.php β audit password resets
- Modify: /var/www/html/eqmon/api/auth/impersonate.php β audit impersonation
- Modify: /var/www/html/eqmon/api/admin/users.php β audit user CRUD
- Modify: /var/www/html/eqmon/api/admin/reset-password.php β audit admin password reset
- Modify: /var/www/html/eqmon/api/ai_chat.php β audit AI interactions
- Modify: /var/www/html/eqmon/api/stream.php β audit SSE connections
Depends on: BT-03
Step 1: Add AuditLogger to middleware.php
In requireApiAuth(), after successful auth validation, add:
require_once __DIR__ . '/AuditLogger.php';
// Inside requireApiAuth(), after $session is validated:
AuditLogger::logApiAccess($session);
On auth failure (401 response), add before the exit:
AuditLogger::log(AuditLogger::CAT_ACCESS, 'api_request', AuditLogger::RESULT_DENIED);
This single integration point captures ALL authenticated API requests.
Step 2: Add audit to login.php
Replace the existing logLoginAttempt() calls with AuditLogger equivalents:
On successful login (before response):
AuditLogger::logAuth('login', AuditLogger::RESULT_SUCCESS, $user['user_id'], $email, $authSource);
On failed login (each failure path):
AuditLogger::logAuth('login', AuditLogger::RESULT_FAILURE, null, $email, null, 'invalid_credentials');
On rate limited:
AuditLogger::logAuth('login', AuditLogger::RESULT_DENIED, null, $email, null, 'rate_limited');
On inactive account:
AuditLogger::logAuth('login', AuditLogger::RESULT_FAILURE, $eqmonUser['user_id'], $email, 'eqmon', 'account_inactive');
Keep the existing logLoginAttempt() for backward compatibility β it writes to login_attempts table which is fine as a secondary record.
Step 3: Add audit to logout.php
require_once __DIR__ . '/../../lib/AuditLogger.php';
AuditLogger::logAuth('logout', AuditLogger::RESULT_SUCCESS, $session['user_id']);
Step 4: Add audit to forgot-password.php and reset-password.php
forgot-password.php:
require_once __DIR__ . '/../../lib/AuditLogger.php';
// After successful token generation and email send:
AuditLogger::logAuth('password_reset_request', AuditLogger::RESULT_SUCCESS, $user['user_id'], $email);
// For non-existent users (still log for anomaly detection, but no user_id):
AuditLogger::logAuth('password_reset_request', AuditLogger::RESULT_SUCCESS, null, $email);
reset-password.php:
require_once __DIR__ . '/../../lib/AuditLogger.php';
// After successful password change:
AuditLogger::logAuth('password_reset', AuditLogger::RESULT_SUCCESS, $user['user_id'], $user['email']);
// On invalid/expired token:
AuditLogger::logAuth('password_reset', AuditLogger::RESULT_FAILURE, null, null, null, 'invalid_token');
Step 5: Add audit to impersonate.php
require_once __DIR__ . '/../../lib/AuditLogger.php';
// On impersonation start:
AuditLogger::logAdmin('impersonation_start', AuditLogger::RESULT_SUCCESS, $session['user_id'], $targetUserId, [
'admin_email' => $session['email'],
'target_email' => $targetUser['email'],
]);
// On impersonation denied:
AuditLogger::logAdmin('impersonation_start', AuditLogger::RESULT_DENIED, $session['user_id'], $targetUserId);
Step 6: Add audit to api/admin/users.php
User creation:
AuditLogger::logAdmin('user_create', AuditLogger::RESULT_SUCCESS, $session['user_id'], $newUserId, [
'email' => $input['email'],
'role' => $input['role'],
]);
User deactivation:
AuditLogger::logAdmin('user_deactivate', AuditLogger::RESULT_SUCCESS, $session['user_id'], $targetUserId);
User deletion:
AuditLogger::logAdmin('user_delete', AuditLogger::RESULT_SUCCESS, $session['user_id'], $targetUserId, [
'email' => $targetUser['email'],
]);
Role change:
AuditLogger::logAdmin('role_change', AuditLogger::RESULT_SUCCESS, $session['user_id'], $targetUserId, [
'old_role' => $oldRole,
'new_role' => $newRole,
]);
Step 7: Add audit to api/admin/reset-password.php
AuditLogger::logAdmin('admin_password_reset', AuditLogger::RESULT_SUCCESS, $session['user_id'], $targetUserId);
Step 8: Add audit to api/ai_chat.php
require_once __DIR__ . '/../lib/AuditLogger.php';
// On chat request (before processing):
AuditLogger::logAI('chat_request', AuditLogger::RESULT_SUCCESS, $session['user_id'], [
'mode' => $mode, // 'general' or 'analysis'
'message_length' => strlen($userMessage),
]);
// If guardrail triggers:
AuditLogger::logAI('guardrail_triggered', AuditLogger::RESULT_DENIED, $session['user_id'], [
'reason' => $guardrailReason,
'pattern' => $matchedPattern,
]);
Step 9: Verify audit trail
# Login as test user
curl -s -X POST http://localhost:8081/eqmon/api/auth/login.php \
-H 'Content-Type: application/json' \
-d '{"email":"redteam-sysadmin@test.com","password":"RedTeam$ysAdmin2026!"}'
# Check audit events
sudo -u postgres psql -d eqmon -c "SELECT timestamp, category, action, result, ip_address FROM audit_events ORDER BY timestamp DESC LIMIT 10;"
Expected: auth/login success event + any api_request events.
Step 10: Commit
cd /var/www/html/eqmon
git add -A
git commit -m "feat: integrate AuditLogger into all endpoints (NIST 3.3.1, 3.3.2, 3.1.7)"